package com.zqz.webserver.herocat;

import com.zqz.webserver.servlet.HeroServlet;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.sctp.nio.NioSctpServerChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CustomServer {

    private final String CLASS_SUFFIX = ".class";

    private final Map<String, HeroServlet> servletMap = new ConcurrentHashMap<>();
    private final Map<String, String> classNameMap = new HashMap<>();

    private final String basePackage;

    public CustomServer(String basePackage) {
        this.basePackage = basePackage;
    }

    public void start() throws Exception {
        cacheClassName(basePackage);
        runServer();
    }

    private void cacheClassName(String basePackage) {
        URL resource = Thread.currentThread().getContextClassLoader()
                .getResource(basePackage.replaceAll("\\.", "/"));

        if (null == resource) {
            return;
        }

        File dir = new File(resource.getFile());
        Arrays.stream(dir.listFiles()).forEach(file -> {
            if (file.isDirectory()) {
                cacheClassName(String.format("%s.%s", basePackage, file.getName()));
            } else if (file.getName().endsWith(CLASS_SUFFIX)) {
                String simpleClassName = file.getName().replace(CLASS_SUFFIX, "").trim();
                System.out.println(String.format("%s.%s", basePackage, simpleClassName));
                classNameMap.put(simpleClassName.toLowerCase(), String.format("%s.%s", basePackage, simpleClassName));
            }
        });
    }

    private void runServer() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel socketChannel) {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new HttpServerCodec());
                            pipeline.addLast(new CustomHandler(servletMap, classNameMap));
                        }
                    });
            int port = initPort();
            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("HeroCat启动成功：监听端口号为:" + port);
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private static int initPort() throws Exception {
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("server.xml");

        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element portEle = (Element) document.selectSingleNode("//port");
        return Integer.parseInt(portEle.getText());
    }
}
